﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using HalconDotNet;
using Go;

namespace fhs
{
    class Vision_Mark : VisionBase
    {
        public class Param : ParamBase
        {
            public class Mark
            {
                public int exposureTime;
                public double score;
                public double offsetRow;
                public double offsetCol;
                public Roi.Rect roi;
                public int matchType;
                public bool enableLine;
                public Roi.Rect hengLine;
                public Roi.Rect shuLine;
                public int threshold;
                public double sigma;
                public double deltaAngle;
                public bool hengTransition;
                public bool shuTransition;
                public bool metrologyLine;
                public HObject markImage;

                public void Load(string vision, string productName)
                {
                    try
                    {
                        string path = $"./VisionConfig/Product/{productName}/{vision}";
                        exposureTime = DefaultLoad.Load(0, $"{path}/ExposureTime.txt", 0);
                        score = DefaultLoad.Load(0.9, $"{path}/Score.txt", 0);
                        offsetRow = DefaultLoad.Load(0, $"{path}/OffsetRow.txt", 0);
                        offsetCol = DefaultLoad.Load(0, $"{path}/OffsetCol.txt", 0);
                        roi.Row = DefaultLoad.Load(0.0, $"{path}/ROI.txt", 0);
                        roi.Col = DefaultLoad.Load(0.0, $"{path}/ROI.txt", 1);
                        roi.Len1 = DefaultLoad.Load(0.0, $"{path}/ROI.txt", 2);
                        roi.Len2 = DefaultLoad.Load(0.0, $"{path}/ROI.txt", 3);
                        roi.Phi = FlatCalib.ToRad(DefaultLoad.Load(0.0, $"{path}/ROI.txt", 4));
                        matchType = DefaultLoad.Load(0, $"{path}/MatchType.txt", 0);
                        enableLine = DefaultLoad.Load(false, $"{path}/EnableLine.txt", 0);
                        hengLine.Row = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 0);
                        hengLine.Col = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 1);
                        hengLine.Len1 = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 2);
                        hengLine.Len2 = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 3);
                        hengLine.Phi = FlatCalib.ToRad(DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 4));
                        shuLine.Row = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 0);
                        shuLine.Col = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 1);
                        shuLine.Len1 = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 2);
                        shuLine.Len2 = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 3);
                        shuLine.Phi = FlatCalib.ToRad(DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 4));
                        threshold = DefaultLoad.Load(60, $"{path}/Threshold.txt", 0);
                        sigma = DefaultLoad.Load(1.0, $"{path}/Sigma.txt", 0);
                        deltaAngle = FlatCalib.ToRad(DefaultLoad.Load(1.0, $"{path}/DeltaAngle.txt", 0));
                        hengTransition = DefaultLoad.Load(false, $"{path}/HengTransition.txt", 0);
                        shuTransition = DefaultLoad.Load(false, $"{path}/ShuTransition.txt", 0);
                        metrologyLine = DefaultLoad.Load(true, $"{path}/MetrologyLine.txt", 0);
                        try
                        {
                            HOperatorSet.ReadImage(out markImage, $"{path}/Mark.bmp");
                        }
                        catch (Exception)
                        {
                            markImage = null;
                        }
                    }
                    catch (System.Exception ex)
                    {
                        LogMgr.Error($"{vision}参数读取异常{ex.Message}");
                    }
                }

                public void Save(string vision, string productName)
                {
                    try
                    {
                        string path = $"./VisionConfig/Product/{productName}/{vision}";
                        Directory.CreateDirectory(path);
                        File.WriteAllLines($"{path}/ExposureTime.txt", new string[] { exposureTime.ToString() });
                        File.WriteAllLines($"{path}/Score.txt", new string[] { score.ToString() });
                        File.WriteAllLines($"{path}/OffsetRow.txt", new string[] { offsetRow.ToString() });
                        File.WriteAllLines($"{path}/OffsetCol.txt", new string[] { offsetCol.ToString() });
                        File.WriteAllLines($"{path}/ROI.txt", new string[] { roi.Row.ToString(), roi.Col.ToString(), roi.Len1.ToString(), roi.Len2.ToString(), FlatCalib.ToDeg(roi.Phi).ToString() });
                        File.WriteAllLines($"{path}/MatchType.txt", new string[] { matchType.ToString() });
                        File.WriteAllLines($"{path}/EnableLine.txt", new string[] { enableLine.ToString() });
                        File.WriteAllLines($"{path}/HengLine.txt", new string[] { hengLine.Row.ToString(), hengLine.Col.ToString(), hengLine.Len1.ToString(), hengLine.Len2.ToString(), FlatCalib.ToDeg(hengLine.Phi).ToString() });
                        File.WriteAllLines($"{path}/ShuLine.txt", new string[] { shuLine.Row.ToString(), shuLine.Col.ToString(), shuLine.Len1.ToString(), shuLine.Len2.ToString(), FlatCalib.ToDeg(shuLine.Phi).ToString() });
                        File.WriteAllLines($"{path}/Threshold.txt", new string[] { threshold.ToString() });
                        File.WriteAllLines($"{path}/Sigma.txt", new string[] { sigma.ToString() });
                        File.WriteAllLines($"{path}/DeltaAngle.txt", new string[] { FlatCalib.ToDeg(deltaAngle).ToString() });
                        File.WriteAllLines($"{path}/HengTransition.txt", new string[] { hengTransition.ToString() });
                        File.WriteAllLines($"{path}/ShuTransition.txt", new string[] { shuTransition.ToString() });
                        File.WriteAllLines($"{path}/MetrologyLine.txt", new string[] { metrologyLine.ToString() });
                        if (null != markImage)
                        {
                            try
                            {
                                HOperatorSet.WriteImage(markImage, "bmp", 0, $"{path}/Mark.bmp");
                            }
                            catch (Exception) { }
                        }
                    }
                    catch (System.Exception ex)
                    {
                        LogMgr.Error($"{vision}参数保存异常{ex.Message}");
                    }
                }
            }

            public Dictionary<VisionName.Camera, Mark> VisionConfig = new Dictionary<VisionName.Camera, Mark>();

            public override VisionName.Vision Name
            {
                get
                {
                    return VisionName.Vision.Mark;
                }
            }

            public Param()
            {
                foreach (VisionName.Camera camera in Enum.GetValues(typeof(VisionName.Camera)))
                {
                    VisionConfig[camera] = new Mark();
                }
            }

            public void Load(VisionName.Camera camera, string productName)
            {
                VisionConfig[camera].Load($"{Name}/{camera}", productName);
            }

            public override void Load(string productName)
            {
                try
                {
                    foreach (var item in VisionConfig)
                    {
                        item.Value.Load($"{Name}/{item.Key}", productName);
                    }
                }
                catch (System.Exception ex)
                {
                    LogMgr.Error($"{Name}参数读取异常{ex.Message}");
                }
            }

            public override void Save(string productName)
            {
                try
                {
                    foreach (var item in VisionConfig)
                    {
                        item.Value.Save($"{Name}/{item.Key}", productName);
                    }
                }
                catch (System.Exception ex)
                {
                    LogMgr.Error($"{Name}参数保存异常{ex.Message}");
                }
            }
        }

        public override VisionName.Vision Name
        {
            get
            {
                return VisionName.Vision.Mark;
            }
        }

        public class Result : ResultBase
        {
            public bool error;
            public bool markOK;
            public MyPoint mark;
            public MyPoint markEx;
            public MyPoint shuMark;
            public MyPoint hengMark;
            public HObject image;
        }

        public class Option : OptionBase
        {
            public VisionName.Camera camera;
            public HTuple modelID;
            public HObject image;
            public int delay;
        }

        protected override async Task<ResultBase> RefPriview(CameraControl mainView, HObject[] images, ParamBase param)
        {
            mainView.Clear();
            viewObjects.value.drawObjects.Clear();
            viewObjects.value.drawTexts.Clear();
            mainView.ShowImage(images[0]);
            return null;
        }

        public VisionName.Camera PreviewCamera = (VisionName.Camera)(-1);

        static public HTuple CreateShapeModel(HObject markImage)
        {
            HTuple modelID;
            HOperatorSet.CreateShapeModel(markImage, "auto", FlatCalib.ToRad(-3), FlatCalib.ToRad(6), FlatCalib.ToRad(0.1), "auto", "use_polarity", "auto", "auto", out modelID);
            return modelID;
        }

        static public HTuple CreateNccModel(HObject markImage)
        {
            HTuple modelID;
            HOperatorSet.CreateNccModel(markImage, "auto", FlatCalib.ToRad(-3), FlatCalib.ToRad(6), FlatCalib.ToRad(0.1), "use_polarity", out modelID);
            return modelID;
        }

        static public void ClearShapeModel(HTuple modelID)
        {
            HOperatorSet.ClearShapeModel(modelID);
        }

        static public void ClearNccModel(HTuple modelID)
        {
            HOperatorSet.ClearNccModel(modelID);
        }

        protected override async Task<ResultBase> Translate(bool isPreview, CameraControl mainView, ParamBase paramBase, OptionBase optionBase)
        {
            ViewObjects viewObjects = this.viewObjects.value;
            Option option = (Option)optionBase;
            Param param = (Param)paramBase;
            if (null == param)
            {
                param = await generator.send_task(() => (Param)LoadParam(ProductMgr.currProduct));
            }
            VisionName.Camera cameraName = null != option ? option.camera : PreviewCamera;
            Param.Mark markParam = param.VisionConfig[cameraName];
            viewObjects.drawObjects.Clear();
            viewObjects.drawTexts.Clear();
            long beginTick = system_tick.ms;
            if (isPreview)
            {
                try
                {
                    CameraBase camera = CameraMgr.cameras[cameraName];
                    await generator.send_service(camera.optionWork.service, () => camera.SetExposureTime(markParam.exposureTime));
                    if (null != option && option.delay > 0)
                    {
                        await generator.sleep(option.delay);
                    }
                    HObject snapImage = await generator.send_service(camera.optionWork.service, () => camera.Snap());
                    LogMgr.Info($"{cameraName} 取图用时 {system_tick.ms - beginTick}ms");
                    if (null != snapImage)
                    {
                        mainView.ShowImage(snapImage);
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "Main", obj = snapImage });
                    }
                    else
                    {
                        mainView.Clear();
                        LogMgr.Error($"{cameraName}取图失败");
                    }
                }
                catch (generator.stop_exception) { throw; }
                catch (Exception)
                {
                    mainView.Clear();
                    LogMgr.Error($"{cameraName}取图失败");
                }
                return null;
            }
            HObject Image = option.image;
            if (null == option.image)
            {
                try
                {
                    CameraBase camera = CameraMgr.cameras[cameraName];
                    await generator.send_service(camera.optionWork.service, () => camera.SetExposureTime(markParam.exposureTime));
                    if (option.delay > 0)
                    {
                        await generator.sleep(option.delay);
                    }
                    Image = await generator.send_service(camera.optionWork.service, () => camera.Snap());
                    LogMgr.Info($"{cameraName} 取图用时 {system_tick.ms - beginTick}ms");
                }
                catch (generator.stop_exception) { throw; }
                catch (Exception) { }
                if (null == Image)
                {
                    mainView.Clear();
                    LogMgr.Error($"{cameraName}取图失败");
                    return null;
                }
            }
            mainView.Clear();
            mainView.ShowImage(Image);
            viewObjects.drawObjects.AddLast(new ObjectShow { text = "Main", obj = Image });
            Result result = new Result { image = Image };
            generator.lock_shield();
            bool findOk = await generator.send_service(genWork.service, delegate ()
            {
                int matchType = markParam.matchType;
                HTuple modelID = option.modelID;
                try
                {
                    HObject roi;
                    HOperatorSet.GenRectangle2ContourXld(out roi, markParam.roi.Row, markParam.roi.Col, markParam.roi.Phi, markParam.roi.Len1, markParam.roi.Len2);
                    viewObjects.drawObjects.AddLast(new ObjectShow { text = "ROI", obj = roi, color = Color.Lime, margin = true });
                    if (null == option.modelID)
                    {
                        switch (matchType)
                        {
                            case 0:
                                modelID = CreateShapeModel(markParam.markImage);
                                break;
                            case 1:
                                modelID = CreateNccModel(markParam.markImage);
                                break;
                            default:
                                return false;
                        }
                    }
                    HObject modelContours = null;
                    if (0 == matchType)
                    {
                        HOperatorSet.GetShapeModelContours(out modelContours, modelID, 1);
                        var fourDots = AOI.CalcROIDots(markParam.roi);
                        HTuple homMat;
                        HObject roiModel;
                        HOperatorSet.HomMat2dIdentity(out homMat);
                        HOperatorSet.HomMat2dTranslate(homMat, fourDots.value2.Row, fourDots.value2.Col, out homMat);
                        HOperatorSet.AffineTransContourXld(modelContours, out roiModel, homMat);
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "ModelContours", obj = roiModel, color = Color.Lime, margin = true });
                    }
                    HObject reduceRegion, reduceImage;
                    HOperatorSet.GenRegionContourXld(roi, out reduceRegion, "filled");
                    HOperatorSet.ReduceDomain(Image, reduceRegion, out reduceImage);
                    HTuple markRow, markCol, markAngle, markScroe;
                    switch (matchType)
                    {
                        case 0:
                            HOperatorSet.FindShapeModel(reduceImage, modelID, FlatCalib.ToRad(-3), FlatCalib.ToRad(6), 0.5 * markParam.score, 1, 0, "least_squares", 0, 0.8, out markRow, out markCol, out markAngle, out markScroe);
                            break;
                        case 1:
                            HOperatorSet.FindNccModel(reduceImage, modelID, FlatCalib.ToRad(-3), FlatCalib.ToRad(6), 0.5 * markParam.score, 1, 0, "true", 0, out markRow, out markCol, out markAngle, out markScroe);
                            break;
                        default:
                            return false;
                    }
                    if (1 != markScroe.Length)
                    {
                        viewObjects.drawTexts.AddLast(WindowTexts.Make("No Mark", false, 0, 0, Color.Red, true, Color.Black));
                        return false;
                    }
                    HTuple offsetRow, offsetCol;
                    {
                        HTuple homMat;
                        HOperatorSet.HomMat2dIdentity(out homMat);
                        HOperatorSet.HomMat2dRotate(homMat, markAngle, markRow, markCol, out homMat);
                        HOperatorSet.AffineTransPoint2d(homMat, markRow + markParam.offsetRow, markCol + markParam.offsetCol, out offsetRow, out offsetCol);
                    }
                    if (0 == matchType)
                    {
                        HTuple homMat;
                        HObject transContours;
                        HOperatorSet.HomMat2dIdentity(out homMat);
                        HOperatorSet.HomMat2dTranslate(homMat, markRow, markCol, out homMat);
                        HOperatorSet.HomMat2dRotate(homMat, markAngle, markRow, markCol, out homMat);
                        HOperatorSet.AffineTransContourXld(modelContours, out transContours, homMat);
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "TransContours", obj = transContours, color = Color.Blue, margin = true });
                    }
                    bool ok = result.markOK = markScroe >= markParam.score;
                    result.hengMark = result.shuMark = result.mark = result.markEx = new MyPoint { Row = offsetRow, Col = offsetCol };
                    HObject markCross;
                    HOperatorSet.GenCrossContourXld(out markCross, offsetRow, offsetCol, 60, FlatCalib.ToRad(45));
                    viewObjects.drawObjects.AddLast(new ObjectShow { text = "MarkCross", obj = markCross, color = ok ? Color.Lime : Color.Red, margin = true });
                    viewObjects.drawTexts.AddLast(WindowTexts.Make($"Mark: {markScroe.D.ToString("0.00")},({(int)offsetRow.D},{(int)offsetCol.D})", true, (int)offsetRow.D, (int)offsetCol.D, ok ? Color.Lime : Color.Red, false, Color.White));
                    if (ok && markParam.enableLine)
                    {
                        AOI.FindLineResult hengRes, shuRes;
                        Roi.Rect hengLine = new Roi.Rect { Row = markParam.hengLine.Row + offsetRow, Col = markParam.hengLine.Col + offsetCol, Phi = markParam.hengLine.Phi + markAngle, Len1 = markParam.hengLine.Len1, Len2 = markParam.hengLine.Len2 };
                        Roi.Rect shuLine = new Roi.Rect { Row = markParam.shuLine.Row + offsetRow, Col = markParam.shuLine.Col + offsetCol, Phi = markParam.shuLine.Phi + markAngle, Len1 = markParam.shuLine.Len1, Len2 = markParam.shuLine.Len2 };
                        if (markParam.metrologyLine)
                        {
                            hengRes = AOI.FindLine2(Image, hengLine, 30, markParam.sigma, markParam.threshold, markParam.hengTransition);
                            shuRes = AOI.FindLine2(Image, shuLine, 30, markParam.sigma, markParam.threshold, markParam.shuTransition);
                        }
                        else
                        {
                            hengRes = AOI.FindLine(Image, hengLine, 30, markParam.sigma, markParam.threshold, markParam.hengTransition);
                            shuRes = AOI.FindLine(Image, shuLine, 30, markParam.sigma, markParam.threshold, markParam.shuTransition);
                        }
                        if (null != hengRes.lineContour && null != shuRes.lineContour)
                        {
                            HTuple angle, row, col, isP;
                            HObject startCross1, startCross2, interCross;
                            AOI.FindLineResult markRes = new AOI.FindLineResult();
                            HOperatorSet.GenCrossContourXld(out startCross1, hengRes.line.value1.Row, hengRes.line.value1.Col, 60, FlatCalib.ToRad(45));
                            HOperatorSet.GenCrossContourXld(out startCross2, shuRes.line.value1.Row, shuRes.line.value1.Col, 60, FlatCalib.ToRad(45));
                            HOperatorSet.ConcatObj(hengRes.roi, shuRes.roi, out markRes.roi);
                            HOperatorSet.ConcatObj(hengRes.lineContour, shuRes.lineContour, out markRes.lineContour);
                            HOperatorSet.ConcatObj(hengRes.measCross, shuRes.measCross, out markRes.measCross);
                            HOperatorSet.ConcatObj(markRes.lineContour, startCross1, out markRes.lineContour);
                            HOperatorSet.ConcatObj(markRes.lineContour, startCross2, out markRes.lineContour);
                            viewObjects.drawObjects.AddLast(new ObjectShow { text = "MeasROIs", obj = markRes.roi, color = Color.Lime, margin = true });
                            viewObjects.drawObjects.AddLast(new ObjectShow { text = "LineContour", obj = markRes.lineContour, color = Color.Red, margin = true });
                            viewObjects.drawObjects.AddLast(new ObjectShow { text = "MeasCross", obj = markRes.measCross, color = Color.Blue, margin = true });
                            HOperatorSet.AngleLl(
                                hengRes.line.value1.Row, hengRes.line.value1.Col, hengRes.line.value2.Row, hengRes.line.value2.Col,
                                shuRes.line.value1.Row, shuRes.line.value1.Col, shuRes.line.value2.Row, shuRes.line.value2.Col, out angle);
                            HOperatorSet.IntersectionLl(
                                hengRes.line.value1.Row, hengRes.line.value1.Col, hengRes.line.value2.Row, hengRes.line.value2.Col,
                                shuRes.line.value1.Row, shuRes.line.value1.Col, shuRes.line.value2.Row, shuRes.line.value2.Col, out row, out col, out isP);
                            result.markEx = new MyPoint { Row = row, Col = col };
                            result.hengMark = 0.5 * (hengRes.line.value1 + hengRes.line.value2);
                            result.shuMark = 0.5 * (shuRes.line.value1 + shuRes.line.value2);
                            HOperatorSet.GenCrossContourXld(out interCross, row, col, 60, FlatCalib.ToRad(45));
                            viewObjects.drawObjects.AddLast(new ObjectShow { text = "InterCross", obj = interCross, color = Color.Blue, margin = true });
                            double deltaAngle = Math.Abs(90.0 - Math.Abs(FlatCalib.NorAngle(FlatCalib.ToDeg(angle))));
                            bool deltaOk = deltaAngle <= FlatCalib.ToDeg(markParam.deltaAngle);
                            if (!deltaOk)
                            {
                                LogMgr.Error($"{cameraName}角差异常，定位失败");
                            }
                            viewObjects.drawTexts.AddLast(WindowTexts.Make($"{deltaAngle.ToString("0.0")},({(int)row.D},{(int)col.D})", true, (int)row.D, (int)col.D, deltaOk ? Color.Lime : Color.Red, false, Color.White));
                            return deltaOk;
                        }
                        LogMgr.Error($"{cameraName}定位失败");
                        if (null != hengRes.roi) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengROIs", obj = hengRes.roi, color = Color.Lime, margin = true });
                        if (null != shuRes.roi) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuROIs", obj = shuRes.roi, color = Color.Lime, margin = true });
                        if (null != hengRes.lineContour) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengLineContour", obj = hengRes.lineContour, color = Color.Red, margin = true });
                        if (null != shuRes.lineContour) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuLineContour", obj = shuRes.lineContour, color = Color.Red, margin = true });
                        if (null != hengRes.measCross) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengMeasCross", obj = hengRes.measCross, color = Color.Blue, margin = true });
                        if (null != shuRes.measCross) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuMeasCross", obj = shuRes.measCross, color = Color.Blue, margin = true });
                        return false;
                    }
                    return ok;
                }
                catch (Exception ec)
                {
                    LogMgr.File($"{cameraName}查找Mark失败{ec.Message}\r\n{ec.StackTrace}");
                    return false;
                }
                finally
                {
                    if (null == option.modelID && null != modelID)
                    {
                        switch (matchType)
                        {
                            case 0:
                                ClearShapeModel(modelID);
                                break;
                            case 1:
                                ClearNccModel(modelID);
                                break;
                        }
                    }
                }
            });
            await generator.unlock_shield();
            LogMgr.Info($"{cameraName} 共计用时 {system_tick.ms - beginTick}ms");
            result.error = !findOk;
            return result;
        }

        public override ParamBase LoadParam(string productName)
        {
            Param result = new Param();
            result.Load(productName);
            return result;
        }
    }
}
